#include <linux/module.h>
#include <linux/platform_device.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>

#include "git_version.h"
#include "codec_v4l2_fops.h"

#define CODEC_INFO(fmt, arg...)  printk(KERN_INFO fmt, ## arg)

#define DRIVER_NAME     "codec"

struct codec_dev {
    struct device *dev;
    struct video_device vdev;
    struct v4l2_device v4l2_dev;
};

static const struct v4l2_file_operations codec_v4l2_fops = {
    .owner          = THIS_MODULE,
    .open           = codec_v4l2_open,
    .release        = codec_v4l2_release,
    .poll           = codec_v4l2_poll,
    .unlocked_ioctl = video_ioctl2, // ?
    .mmap           = codec_v4l2_mmap
};

static int codec_probe(struct platform_device *pdev)
{
    struct codec_dev *codec;
    int ret;

    codec = devm_kzalloc(&pdev->dev, sizeof(*codec), GFP_KERNEL);
    if (codec == NULL)
        return -EINVAL;

    platform_set_drvdata(pdev, codec); // pdev->dev->driver_data

    codec->dev = &pdev->dev;

    ret = v4l2_device_register(codec->dev, &codec->v4l2_dev);
    if (ret != 0) {
        CODEC_INFO("Failed to regitster V4L2 device. ret=%d.", ret);
        return ret;
    }

    codec->vdev.fops     = &codec_v4l2_fops;
    codec->vdev.v4l2_dev = &codec->v4l2_dev;
    codec->vdev.release  = video_device_release_empty;
    strncpy(codec->vdev.name, DRIVER_NAME, sizeof(DRIVER_NAME) - 1);

    // video_set_drvdata(&codec->vdev, codec);

    ret = video_register_device(&codec->vdev, VFL_TYPE_GRABBER, -1); // ?
    if (ret != 0) {
        CODEC_INFO("Failed to regitster video device. ret=%d.", ret);
        goto unregister_device;
    }

    return 0;

unregister_device:
    v4l2_device_unregister(&codec->v4l2_dev);

    return ret;
}

static int codec_remove(struct platform_device *pdev)
{
    struct codec_dev *codec;

    codec = platform_get_drvdata(pdev);
    platform_set_drvdata(pdev, NULL);

    video_unregister_device(&codec->vdev);
    v4l2_device_unregister(&codec->v4l2_dev);

    devm_kfree(&pdev->dev, codec);

    return 0;
}

static struct platform_driver codec_driver = {
    .probe  = codec_probe,
    .remove = codec_remove,
    .driver = {
        .name   = DRIVER_NAME,
        .owner  = THIS_MODULE,
    },
};

static int __init codec_init(void)
{
    int ret;

    CODEC_INFO("codec init.\n");
    ret = platform_driver_register(&codec_driver);
    if (ret != 0) {
        printk("platform register failed\n");
        return ret;
    }
    return 0;
}

static void __exit codec_exit(void)
{
    CODEC_INFO("codec exit.\n");
    platform_driver_unregister(&codec_driver);
}


module_init(codec_init);
module_exit(codec_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Chongyu.Guo");
MODULE_DESCRIPTION("codec: v4l2 driver demo. " GIT_VERSION);
